#pragma once

#include <vex/core/contract/handle.h>
#include <vex/iterable/range_tags.h>

namespace vex { namespace iterable { namespace contract {
    template<class TCategory = trivial_range_tag, class TRef = void>
    struct VEX_ABSTRACT range : virtual public core::contract::handle
    {
        virtual bool move_front() = 0;
        VEX_PROTECT_CONTRACT(range);
    };

    template<class TRef>
    struct VEX_ABSTRACT range<output_range_tag, TRef>
        : public range<>
    {
        virtual bool put(TRef) = 0;
        VEX_PROTECT_CONTRACT(range);
    };

    template<class TRef> 
    struct VEX_ABSTRACT range<one_pass_range_tag, TRef>
        : public range<trivial_range_tag>
    {
        virtual TRef front() const = 0;
        VEX_PROTECT_CONTRACT(range);
    };

    template<class TRef>
    struct VEX_ABSTRACT range<forward_range_tag, TRef>
        : public range<one_pass_range_tag, TRef>
    {
        virtual range<forward_range_tag, TRef>* save() const = 0;
        VEX_PROTECT_CONTRACT(range);
    };

    template<class TRef>
    struct VEX_ABSTRACT range<bidirectional_range_tag, TRef>
        : public range<forward_range_tag, TRef>
    {
        virtual range<bidirectional_range_tag, TRef>* save() const = 0;
        virtual TRef back() const = 0;
        virtual bool move_back() = 0;
        VEX_PROTECT_CONTRACT(range);
    };

    template<class TRef>
    struct VEX_ABSTRACT range<random_access_range_tag, TRef>
        : public range<bidirectional_range_tag, TRef>
    {
        virtual range<random_access_range_tag, TRef>* save() const = 0;
        virtual bool move_front_of(std::size_t) = 0;
        virtual bool move_back_of(std::size_t) = 0;
        virtual TRef at(std::size_t) const = 0;
        virtual std::size_t size() const = 0;
        VEX_PROTECT_CONTRACT(range);
    };
}}}